package com.lsh.oms.worker.service.task;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dangdang.ddframe.job.api.JobExecutionMultipleShardingContext;
import com.dangdang.ddframe.job.plugin.job.type.dataflow.AbstractIndividualThroughputDataFlowElasticJob;
import com.lsh.atp.api.model.hold.HoldResponse;
import com.lsh.atp.api.model.inventory.RestoreInventoryRequest;
import com.lsh.atp.api.service.inventory.IInventoryRestoreRpcService;
import com.lsh.base.trace.id.filter.TraceIdSetter;
import com.lsh.oms.core.constant.AtpConstant;
import com.lsh.oms.core.constant.ShortMessageConstants;
import com.lsh.oms.core.dao.order.OrderHeadDao;
import com.lsh.oms.core.enums.*;
import com.lsh.oms.core.model.order.OrderHead;
import com.lsh.oms.core.model.payment.BillPaymentInfo;
import com.lsh.oms.core.model.po.OmsTask;
import com.lsh.oms.core.proxy.handler.AsyncTaskHandler;
import com.lsh.oms.core.proxy.handler.OrderConcurrentHandler;
import com.lsh.oms.core.proxy.service.EsProxy;
import com.lsh.oms.core.service.payment.BillPaymentService;
import com.lsh.oms.core.service.payment.PaymentService;
import com.lsh.oms.core.service.po.OmsTaskService;
import com.lsh.oms.core.service.sms.ShortMessageService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.text.MessageFormat;
import java.util.*;

/**
 * Project Name: OrderPaymentCancelTask
 * 北京链商电子商务有限公司
 *
 * @author wangliutao
 * Date: 18/5/30
 * Package Name: com.lsh.oms.worker.service.task
 * Description:
 */
@Component
public class OrderPaymentCancelTask extends AbstractIndividualThroughputDataFlowElasticJob<OrderHead> {
    private static final Logger logger = LoggerFactory.getLogger(OrderPaymentCancelTask.class);

    @Autowired
    private OrderHeadDao orderHeadDao;

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private OmsTaskService omsTaskService;

    @Autowired
    private BillPaymentService billPaymentService;

    @Autowired
    private IInventoryRestoreRpcService restoreService;

    @Autowired
    private EsProxy esProxy;

    @Autowired
    private ShortMessageService shortMessageService;

    @Value("${short.message.order.cancel.project}")
    private String project;

    @Value("${run.system}")
    private String system;

    @Override
    public List<OrderHead> fetchData(JobExecutionMultipleShardingContext context) {
        TraceIdSetter.set();
        logger.info("[订单支付超时] [fetchData] start! context:{}", context.toString());
        int fetchSize = context.getFetchDataCount();
        Map<Integer, String> items = context.getShardingItemParameters();
        List<OrderHead> orders = new ArrayList<>();
        for (String value : items.values()) {
            String[] array = value.split(";");
            Integer regionCode = Integer.valueOf(array[0]);
            int timeOffset = Integer.parseInt(array[1]);
            logger.info("[订单支付超时] [fetchData] regionCode = {}, timeOffset = {}", regionCode, timeOffset);

            Set<Integer> payTypes = new HashSet<>();
            payTypes.add(PayType.ONLINE_PAYMENT.getValue());
            payTypes.add(PayType.OPEN_ACCOUNT.getValue());
            payTypes.add(PayType.PRE_PAYMENT.getValue());

            Integer time = ((int) (System.currentTimeMillis() / 1000)) - 300;

            List<OrderHead> list = orderHeadDao.findListByRegionAndStatusAndPayStatus(null, OrderStatus.ORDER_NEW.getIndex(), payTypes, PayStatus.NOT_PAID.getValue(), time, fetchSize);
            orders.addAll(list);
        }

        logger.info("[订单支付超时] [fetchData] end! size = {}", orders.size());
        return orders;
    }

    @Override
    public boolean processData(JobExecutionMultipleShardingContext context, OrderHead orderHead) {
        if (orderHead == null || orderHead.getOrderCode() == null) {
            return true;
        }

        OrderHead orderHeadForUpdate = new OrderHead();
        Long orderCode = orderHead.getOrderCode();
        try {
            if (orderHead.getPayType().equals(PayType.PAY_ON_DELIVERY.getValue())) {
                logger.info("[订单支付超时] [processData] order payType is wrong! orderCode: {}", orderCode);
                return true;
            }
            if (orderHead.getPayStatus().equals(PayStatus.PAID.getValue())) {
                logger.info("[订单支付超时] [processData] order already pay! orderCode: {}", orderCode);
                return true;
            }
            if (paymentService.exists(orderHead.getOrderCode())) {
                logger.info("[订单支付超时] [processData] order pay buffer! orderCode: {}", orderCode);
                return true;
            }
            if (!OrderConcurrentHandler.lockOrder(orderHead.getOrderCode())) {
                logger.info("[订单支付超时] [processData] lock failed, orderCode: {}", orderCode);
                return true;
            }

            logger.info("[订单支付超时] [processData] update order pay status [start], orderCode: {}", orderCode);

            orderHeadForUpdate.setId(orderHead.getId());
            orderHeadForUpdate.setOrderCode(orderCode);
            orderHeadForUpdate.setOrderStatus(OrderStatus.ORDER_CANCEL_SYSTEM.getIndex());
            orderHeadForUpdate.setUpdateTime((int) (System.currentTimeMillis() / 1000));
            // 系统取消,通知商城;商城判断是否需要退券
            paymentService.orderTimeoutCancelAndNotfyShop(orderHeadForUpdate, orderHead.getVenderId());
            logger.info("[订单支付超时] [processData] update order pay status [end], orderForUpdate: {}", JSON.toJSONString(orderHeadForUpdate));
            // 判断是否需要退款（包括钱包）
            this.addRefundTask(orderCode, orderHead.getVenderId());

            this.atpRestoreInventory(Collections.singletonList(orderHead), "[订单支付超时] [processData]");
            logger.info("[订单支付超时] [processData] insert operate log, orderCode: {}", orderCode);
            this.paymentService.addOperateLog(orderHead, OrderOperateType.ORDER_CANCEL_BY_SYSTEM, OrderStatus.ORDER_CANCEL_SYSTEM.getIndex());
            // TODO: 异步更新ES
            String uuid = UUID.randomUUID().toString();
            OrderHead esOrderHead = new OrderHead();
            esOrderHead.setOrderCode(orderHead.getOrderCode());
            esOrderHead.setOrderStatus(OrderStatus.ORDER_CANCEL_SYSTEM.getIndex());
            logger.info("[订单支付超时] [processData] Async update ElasticSearch! uuid: {}, orderCode: {}", uuid, orderCode);
            AsyncTaskHandler.executeAsyncTask(this.esProxy.newAsyncEditTask(EsProxy.transformToOrderHeadForEs(esOrderHead), uuid));

            // TODO:短信提醒(短信模版)
            JSONObject jsonObject = JSONObject.parseObject(orderHead.getAddressInfo());
            String contactPhone = jsonObject.getString("contact_phone");
            if (!StringUtils.isBlank(contactPhone)) {
                String content = MessageFormat.format(ShortMessageConstants.CANCEL_MSG, String.valueOf(orderCode));

                logger.info("[订单支付超时] [processData] send short message to customer, contact_phone: {}, orderCode: ", contactPhone, orderCode);
                shortMessageService.asyncSendMessage(new ShortMessageService().new ShortMessage(contactPhone, content));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            // 邮件报警
//            String title = "[" + system + "][订单支付超时]";
//            String[] persons = {"aop@lsh123.com"};
//            StringBuilder content = new StringBuilder("orderCode: ");
//            content.append(orderCode).append("\n").append("message: ").append(e.getMessage());
//            OmsEventBus.post(new EmailModel(content.toString(), title, persons));
        } finally {
            try {
                OrderConcurrentHandler.unlockOrder(orderCode);
            } catch (Exception e) {
                logger.error("[订单支付超时] [processData] unlock exception", e);
            }
        }

        logger.info("[订单支付超时] [processData] end");
        return true;
    }

    /**
     * 订单超时取消,还原库存
     *
     * @param heads
     * @param logSign
     */
    private void atpRestoreInventory(List<OrderHead> heads, String logSign) {
        for (OrderHead head : heads) {
            // 释放库存
            RestoreInventoryRequest request = new RestoreInventoryRequest();
            request.setChannel(AtpConstant.ATP_CHANNEL_CODE);
            request.setSequence(head.getOrderCode().toString());
            request.setIsFulfill(false);
            logger.info(logSign + " 还原库存, request is - " + JSON.toJSONString(request));

            try {
                HoldResponse resp = restoreService.restoreInventory(request);
                logger.info(logSign + " atp response is - " + resp.getStatus() + " , msg is " + resp.getMeg());

                boolean atpFlag = resp.getStatus() == 0 || resp.getStatus().equals(2001006);
                logger.info(logSign + " 释放库存结果: {}", atpFlag);
            } catch (Exception e) {
                logger.error(logSign + " restore inventory Exception : " + e.getMessage(), e);
            }
        }
    }

    /**
     * 判断是否需要插入退款的任务
     *
     * @param orderCode
     */
    private void addRefundTask(Long orderCode, Long venderId) {
        BillPaymentInfo payFilter = new BillPaymentInfo();
        payFilter.setBillCode(orderCode);
        payFilter.setPayStatus(PayStatus.PAID.getValue());
        payFilter.setIsValid(1);

        Integer count = billPaymentService.countByFilter(payFilter);

        if (count != null && count.intValue() > 0) {
            OmsTask task = new OmsTask();
            task.setRefCode(orderCode.toString());
            task.setTaskType(TaskType.REFUND.getCode());
            task.setVenderId(venderId);

            OmsTask taskOneByFilter = omsTaskService.getTaskOneByFilter(task);

            if (taskOneByFilter == null) {
                JSONObject ext = new JSONObject();
                ext.put("type", OrderRefundTypeEnum.ORDER_CANCEL_CUSTOMERSERVICE.getValue());
                task.setExt(ext.toJSONString());

                logger.info("[预付--退款] insert oms task start! orderCode: {}", orderCode);
                omsTaskService.add(task);
                logger.info("[预付--退款] insert oms task end! orderCode: {}", orderCode);
            }
        }
    }

}
